/*
 * ==========================================================================*\
 * | $Id$
 * |*-------------------------------------------------------------------------*|
 * | Copyright (C) 2009 Virginia Tech | | This file is part of CloudSpace. | |
 * CloudSpace is free software; you can redistribute it and/or modify | it under
 * the terms of the GNU General Public License as published | by the Free
 * Software Foundation; either version 3 of the License, or | (at your option)
 * any later version. | | CloudSpace is distributed in the hope that it will be
 * useful, | but WITHOUT ANY WARRANTY; without even the implied warranty of |
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | GNU General
 * Public License for more details. | | You should have received a copy of the
 * GNU General Public License | along with CloudSpace; if not, see
 * <http://www.gnu.org/licenses/>.
 * \*==========================================================================
 */

package cloudspace.demoitem;

import java.io.File;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxBinary;
import org.openqa.selenium.firefox.FirefoxDriver;

/**
 * The class  is used to capture the screen shot of a URL.
 * This uses the capability of Selenium library to do this.
 * 
 * It also depends upon Xvfb, firefox to be installed in
 * the cloudspace server.
 * 
 * @author Sunil Kamalakar
 */
public class ScreenshotCapturer {

	//The static constants
	private static final int DISPLAY_NUMBER  = 77;
	private static final String XVFB = "/usr/bin/Xvfb -ac";
	private static final String XVFB_COMMAND = XVFB + " :" + DISPLAY_NUMBER;
	private static long DEFAULT_TIMEOUT = 45; //In seconds
	
	//The process and the driver for the execution.
	private static Process process = null;
	private static WebDriver driver = null;
	
	//The logger
	private static Logger log = Logger.getLogger(ScreenshotCapturer.class);

	/**
	 * External classes should not call this, but instead call captureScreenshot()
	 * @param url
	 * @param fileLoc
	 * @return
	 * @throws Exception
	 */
	static boolean takeScreenShot(String url, String fileLoc) throws Exception {

		log.debug("Starting XVFB");
		process = Runtime.getRuntime().exec(XVFB_COMMAND);

		log.debug("Creating firefox binary");
		FirefoxBinary firefox = new FirefoxBinary();

		firefox.setEnvironmentProperty("DISPLAY", ":" + DISPLAY_NUMBER);
		driver = new FirefoxDriver(firefox, null);

		log.debug("Get URL: " + url);
		driver.get(url);
		
		File scrFile = ( (TakesScreenshot) driver ).getScreenshotAs(OutputType.FILE);
		log.debug("Writing image to file: " + new File(fileLoc).getAbsolutePath());
		FileUtils.copyFile(scrFile, new File(fileLoc));
		
		driver.close();
		process.destroy();
		
		driver = null;
		process = null;

		return true;
	}
	
	public synchronized static void captureScreenshot(String pURI, String iURI) throws Exception {
		captureScreenshot(pURI, iURI, 0l);
	}	
	
	public synchronized static void captureScreenshot(String pURI, String iURI, long timeout) throws Exception {
		final String programURI = pURI;
		final String imageURI = iURI;

		try {
			
			timeout = timeout > 0?timeout:DEFAULT_TIMEOUT;

			ExecutorService executor = Executors.newSingleThreadExecutor();
			executor.invokeAll(Arrays.asList(new Callable<String>() {
			    
				public String call() throws Exception {
			        try {
			        	ScreenshotCapturer.takeScreenShot(programURI, imageURI);
			        } catch (Exception e) {
			        	throw new Exception("Screenshot capture terminated with error\n" + e.getMessage());
			        }       
			        return null;
			    }
			}), timeout, TimeUnit.SECONDS);

			executor.shutdown();
			
			if(process == null && driver == null) {
				log.debug("Regular exit");
			}
			else {
				log.debug("Screenshot Process had to be terminated!!!");
			}			
		}
		catch(Exception e) {
			throw new Exception("Screenshot Capture did not terminate correctly\n" + e.getMessage());
		}
		finally {
			terminate();
		}

	}
	
	private static void terminate() {
		log.debug("Terminating process and driver");
		if(driver != null) {
			log.debug("Ending driver!!!");
			driver.close();
			driver = null;
		}
		if (process != null) {
			log.debug("Ending process!!!");
			process.destroy();	
			process = null;
		}
		log.debug("Terminate complete");
	}
	
	public static void main(String[] argv) throws Exception {
		captureScreenshot(argv[0], argv[1], Long.parseLong(argv[2]));
	}
	
}
